/*
 * @Author: quit
 * @Date: 2023-03-17 03:02:19
 * @LastEditTime: 2023-03-17 05:09:05
 * @Descripttion: 泛型
 * @notice:
 */

/**
 * @description: 什么是泛型
 * 在定义函数、接口或者类的时候,不预先指定具体的类型,而是在使用的时候在指定类型的一种特性
 * 可以提高代码的可重用性,使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据
 */
// any 会丢失信息一般只会在后端返回数据不确定是使用
// function Fn(arg: any): any {
//     return arg;
// }
// 泛型函数 在函数名前添加 <> 可以是任何字符 不会丢失数据类型
// function Fn<T>(arg: T): T {
//     return arg;
// }
// let v1 = Fn('string');
// let v2 = Fn(123);
// let v3 = Fn(false);
// console.log(v1, v2, v3);
// let v4 = Fn<string>('world');
// console.log(v4);

/**
 * @description: 泛型变量
 * 将类型变量(也可以叫做类型参数,泛型变量)当做一个类型使用
 * 可以使用多个泛型变量
 * 泛型变量T是我们约定俗成的,可以是任何字符,也可以是多个也就是多泛型变量的使用
 */
// function fn<A>(arg: A[]): A[] {
//     console.log(arg, arg.length);
//     return arg;
// }
// fn([1, 2, 3]);
// fn(['a', 'b', 'c']);
// 多个的情况
// function fn<T, U>(arg: [T, U]): [U, T] {
//     return [arg[1], arg[0]];
// }
// console.log(fn([1, '2']));
// console.log(fn([[1, 2, 3], { name: 'Mike' }]));

/**
 * @description: 泛型接口
 * 就是将泛型的类型变量( <T> )和我们固定类型的接口 (interfaces) 结合,让接口支持多种类型,使接口更灵活
 */

// let fn1 = function (x: string, y: string): Array<string> {
//     return [x, y];
// };
// interface MyFn {
//     (x: string, y: string): Array<string>;
// }
// let fn2: MyFn = fn1;
// console.log(fn2('aaa', 'bbb'));

// interface MyFn {
//     <T>(x: T, y: T): Array<T>;
// }
// let fn2 = function <T>(x: T, y: T): T[] {
//     return [x, y];
// };
// let f1: MyFn = fn2;
// console.log(fn2('aa', 'bb'));
// console.log(fn2(11, 22));
// console.log(fn2([1, 2, 3], ['a', 'b', 'c']));

// 在使用这种写法时,在实例里可以给函数定义类型,当前函数需要什么类型的数据就通过泛型指定一个类型
// interface MyFn<T> {
//     (x: T, y: T): Array<T>;
// }
// let fn2 = function <T>(x: T, y: T): T[] {
//     return [x, y];
// };
// let f1: MyFn<string> = fn2;
// console.log(f1('aa', 'bb'));
// let f2: MyFn<number> = fn2;
// console.log(f2(1, 2));

/**
 * @description: 泛型类
 * 泛型类看上去和泛型接口差不多,泛型类使用 (<>) 将泛型类包裹,跟在类名后,用于类的类型定义
 * 类的两部分:静态部分和实例部分,泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型
 */
// class Person<T, U> {
//     constructor(name: T, age: U) {
//         this.name = name;
//         this.age = age;
//     }
//     name: T;
//     age: U;
//     static id: number;
//     move(length: U): U {
//         return length;
//     }
// }
// let l = new Person<string, number>('张三', 25);
// console.log(l.name, l.age, l.move(10));

/**
 * @description: 泛型约束
 * 在函数内部使用泛型变量的时候,由于事先不知道它的那种类型,所以不能随意的操作它的属性或方法
 * 使用 泛型 变量T extends 继承 我们定义的接口;约束了必须符合的形状,这样就可以使用我们已知的属性或者方法
 */
interface LengthIn {
    length: number;
}
function fn<T extends LengthIn>(arg: T): T {
    console.log(arg.length);
    return arg;
}
fn('123');
fn([1, 2]);
// fn(true); //  boolean不存在 length 属性

export {};
